#FlyFF O3D parser

import os, sys
from Sanae3D.lib.StructReader import StructReader
from Sanae3D.lib.ModelObject import Model3D

class FlyFF_O3D(Model3D):
    
    def __init__(self, obj3D=None, outFile=None, inFile=None):
        
        super(FlyFF_O3D,self).__init__("XAC")
        self.obj3D = obj3D
        self.inFile = inFile
        self.outFile = outFile
        
    def decrypt_string(self, string):

        new_str = ""
        for char in string:
            new_str = ''.join([new_str, chr(ord(char) ^ 0xCD)])
        return new_str
        
    def read_string(self, n, decrypt=0):
        
        name = self.inFile.read_string(n)
        if decrypt:
            name = self.decrypt_string(name)
        return name
    
    def parse_animation_state(self):
        
        rotation = self.inFile.read_float(4)
        translate = self.inFile.read_float(3)
    
    def parse_bones(self, numAnim):
        
        numBones = self.inFile.read_long()
        print "%d bones" %numBones
        for i in range(numBones):
            trans1 = self.inFile.read_float(16)
            trans2 = self.inFile.read_float(16)
        
        for i in range(numBones):            
            boneName = self.read_string(self.inFile.read_long())
            trans1 = self.inFile.read_float(16)
            trans2 = self.inFile.read_float(16)
            index = self.inFile.read_long()
            
        if numBones:
            unk1 = self.inFile.read_long()
            for i in range(unk1):
                unk5 = self.inFile.read_long()
                if unk5:
                    self.parse_animation_state()
                    i += numAnim
                else:
                    self.inFile.read_float(16)
            
            unk2 = self.inFile.read_long()
            if unk2:
                self.inFile.read_float(16)
                unk3 = self.inFile.read_long()
                
    def parse_materials(self):
        
        numMat = self.inFile.read_long()
        for i in range(numMat):
            rgba = self.inFile.read_float(4)
            ambient = self.inFile.read_float(4)
            diffuse = self.inFile.read_float(4)
            specular = self.inFile.read_float(4)
            self.inFile.seek(8, 1) #null
            textureName = self.read_string(self.inFile.read_long())
            
            self.create_material(i)
            self.add_material_name(i, "Material%d" %i)
            self.add_texture_name(i, textureName)
            
        totalMaterial = max(1, numMat)
        
    def parse_mat_id(self, meshName):
        
        numMatID = self.inFile.read_long()
        for i in range(numMatID):
            startIndex, count, matID = self.inFile.read_long(3)
            self.inFile.seek(124)
            
            for j in range(count):
                self.add_face_material(meshName, j, matID)
            print startIndex, count, matID
            
    def parse_lod_data(self, hasBones, meshName):
        
        xCount, numVerts, faceSize, numFaces, unk = self.inFile.read_long(5)
        if xCount:
            self.inFile.read_float(3 * xcount)
            
        print "%d vertices, %d faces" %(numVerts, numFaces)
        for i in range(numVerts):
            vx, vy, vz = self.inFile.read_float(3)
            if hasBones:
                w1, w2 = self.inFile.read_float(2)
                b1, b2 = self.inFile.read_short(2)
            nx, ny, nz = self.inFile.read_float(3)
            tu, tv = self.inFile.read_float(2)
            self.inFile.read_float(3)
            
            self.create_vertex(meshName, i)
            self.add_coords(meshName, i, [vx, vy, vz])
            self.add_vert_normals(meshName, i, [nx, ny, nz])
            self.add_vert_uv(meshName, i, [tu, tv])
            
        for i in range(numFaces):
            v1, v2, v3 = self.inFile.read_short(3)
            
            self.create_face(meshName, i)
            self.add_face_verts(meshName, i, [v1, v2, v3])
            
        for i in range(numVerts):
            self.inFile.read_short()
        unk1 = self.inFile.read_long()
        self.parse_materials()
        self.parse_mat_id(meshName)
                    
    def parse_invisible_lod(self):
        
        numLod = self.inFile.read_long()
        print "%d invisible lod's" %numLod
        for i in range(numLod):
            matrix = self.inFile.read_float(16)
            
    def parse_visible_lod(self, meshName):
        
        flag, unkCount = self.inFile.read_long(2)
        self.inFile.read_long(unkCount)
        lodIndex, lodLink = self.inFile.read_long(2)
        
        if lodLink != -1:
            pass
        
        translate = self.inFile.read_float(16)
        unk = self.inFile.read_float(16)
        
        hasBones = ((flag & 1 != 0) and (lodLink !=0))
        self.parse_lod_data(hasBones, meshName)
        
    
    def parse_bounding_box(self):
        
        xmin, ymin, zmin, xmax, ymax, zmax, unk = self.inFile.read_float(7)
        
    def parse_file(self):
        '''Main parser method. Can be replaced'''
        
        modelLen = self.inFile.read_byte()
        model = self.read_string(modelLen, decrypt=1)
        version = self.inFile.read_long()
        
        self.inFile.seek(4, 1) #checksum?
        
        elementStartPoint1 = self.inFile.read_float(3)
        elementEndPoint1 = self.inFile.read_float(3)
        if version == 0x16:
            elementStartPoint2 = self.inFile.read_float(3)
            elementEndPoint2 = self.inFile.read_float(3)
            
        self.inFile.read_long(2)
        meshName = self.read_string(16, decrypt=1)
        self.create_object(meshName)
        
        self.parse_bounding_box()
        
        numAnim, unkLen = self.inFile.read_long(2)
        if unkLen > 0:
            self.inFile.read_float(3 * unkLen)
            
        self.parse_invisible_lod()        
        unk1 = self.inFile.read_long()
        
        self.parse_bones(numAnim)
        self.parse_visible_lod(meshName)
        
            
    
def read_file(path):
    '''Read the file'''
    
    openFile = StructReader(open(path, 'rb'))
    obj = FlyFF_O3D(inFile=openFile)
    obj.parse_file()
    openFile.close()
    return obj

def write_file(path):
    
    pass

def definitions():
    '''Return the header, extension, and a description of the format'''
    
    return "", "O3D", "FlyFF O3D"

if __name__ == '__main__':
    
    file1 = "Item_BasiliskFruit.o3d"
    read_file(file1)